﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Zgq.CommonTools
{
    /// <summary>
    /// 缓存帮助类
    /// </summary>
    public static class CacheHelper
    {
        private static Dictionary<string, KeyValuePair<object, DateTime>> _cacheDic = new Dictionary<string, KeyValuePair<object, DateTime>>();
        private static readonly ReaderWriterLockSlim _cacheDicLock = new ReaderWriterLockSlim();

        private static readonly List<string> removeKeysByTaskList = new List<string>();
        private static readonly int taskSleepByClearExpires = 1000 * 60 * 60 * 2;

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TResult">TResult结果</typeparam>
        /// <param name="expiresDt">具体过期时间</param>
        /// <returns></returns>
        public delegate TResult FuncTwoOut<TResult>(out DateTime expiresDt);

        static CacheHelper()
        {
            Task.Factory.StartNew(() =>
            {
                #region 定时清理过期缓存数据
                while (true)
                {
                    try
                    {
                        _cacheDicLock.EnterReadLock();
                        try
                        {
                            foreach (var item in _cacheDic)
                            {
                                var pair = item.Value;
                                if (pair.Value < DateTime.Now)
                                    removeKeysByTaskList.Add(item.Key);
                            }
                        }
                        finally
                        {
                            _cacheDicLock.ExitReadLock();
                        }
                        if (removeKeysByTaskList.Count > 0)
                        {
                            _cacheDicLock.EnterWriteLock();
                            try
                            {
                                foreach (var item in removeKeysByTaskList)
                                {
                                    if (_cacheDic.Keys.Contains(item))
                                        _cacheDic.Remove(item);
                                }
                                removeKeysByTaskList.Clear();
                            }
                            finally
                            {
                                _cacheDicLock.ExitWriteLock();
                            }
                        }
                    }
                    finally
                    {
                        System.Threading.Thread.Sleep(taskSleepByClearExpires);
                    }
                }
                #endregion
            });
        }

        /// <summary>
        /// 根据Key移除缓存
        /// </summary>
        /// <typeparam name="T">缓存数据的类型</typeparam>
        /// <param name="key">缓存Key标识</param>
        /// <returns></returns>
        public static T Get<T>(string key)
        {
            bool isNotReadLock = !(_cacheDicLock.IsReadLockHeld || _cacheDicLock.IsWriteLockHeld);
            if (isNotReadLock)
                _cacheDicLock.EnterReadLock();
            try
            {
                T _t = default(T);
                if (_cacheDic.Keys.Contains(key))
                {
                    var pair = _cacheDic[key];
                    if (pair.Value >= DateTime.Now)
                        _t = (T)_cacheDic[key].Key;
                }

                return _t;
            }
            finally
            {
                if (isNotReadLock && _cacheDicLock.IsReadLockHeld)
                    _cacheDicLock.ExitReadLock();
            }
        }

        /// <summary>
        /// 获取缓存数据
        /// </summary>
        /// <typeparam name="T">缓存数据的类型</typeparam>
        /// <param name="key">缓存Key标识</param>
        /// <param name="valFunc">获取数据的委托</param>
        /// <returns></returns>
        public static T Get<T>(string key, FuncTwoOut<T> valFunc)
        {
            bool isNotReadLock = !(_cacheDicLock.IsReadLockHeld || _cacheDicLock.IsWriteLockHeld);

            bool isNotEquals = true;
            T _t = default(T);
            if (isNotReadLock)
                _cacheDicLock.EnterReadLock();
            try
            {
                if (_cacheDic.Keys.Contains(key))
                {
                    var pair = _cacheDic[key];
                    if (DateTime.Now <= pair.Value)
                    {
                        _t = (T)_cacheDic[key].Key;
                        isNotEquals = false;
                    }
                }
            }
            finally
            {
                if (isNotReadLock && _cacheDicLock.IsReadLockHeld)
                    _cacheDicLock.ExitReadLock();
            }

            if (isNotEquals)
            {
                bool isNotWriteLock = !_cacheDicLock.IsWriteLockHeld;

                if (_cacheDicLock.IsReadLockHeld && isNotWriteLock)
                    _cacheDicLock.ExitReadLock();

                if (isNotWriteLock)
                    _cacheDicLock.EnterWriteLock();
                try
                {
                    DateTime expDateTime;
                    _t = valFunc.Invoke(out expDateTime);
                    _cacheDic[key] = new KeyValuePair<object, DateTime>(_t, expDateTime);

                }
                finally
                {
                    if (isNotWriteLock)
                        _cacheDicLock.ExitWriteLock();
                }
            }

            return _t;

        }

        /// <summary>
        /// 设置缓存
        /// </summary>
        /// <param name="key">缓存Key标识</param>
        /// <param name="expDateTime">具体过期时间</param>
        /// <param name="value">值</param>
        public static void Set(string key, object value, DateTime expDateTime)
        {
            bool isNotWriteLock = !_cacheDicLock.IsWriteLockHeld;
            if (isNotWriteLock)
                _cacheDicLock.EnterWriteLock();
            try
            {
                KeyValuePair<object, DateTime> pair = new KeyValuePair<object, DateTime>(value, expDateTime);

                _cacheDic[key] = pair;
            }
            finally
            {
                if (isNotWriteLock)
                    _cacheDicLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// 根据Key移除缓存
        /// </summary>
        /// <param name="key">缓存Key标识</param>
        public static void Remove(string key)
        {
            bool isNotWriteLock = !_cacheDicLock.IsWriteLockHeld;
            if (isNotWriteLock)
                _cacheDicLock.EnterWriteLock();
            try
            {
                _cacheDic.Remove(key);
            }
            finally
            {
                if (isNotWriteLock)
                    _cacheDicLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// 清空缓存
        /// </summary>
        public static void Clear()
        {
            bool isNotWriteLock = !_cacheDicLock.IsWriteLockHeld;
            if (isNotWriteLock)
                _cacheDicLock.EnterWriteLock();
            try
            {
                _cacheDic.Clear();
            }
            finally
            {
                if (isNotWriteLock)
                    _cacheDicLock.ExitWriteLock();
            }
        }
    }
}
